import copy
from collections import OrderedDict
import inspect

from my_typing import *
from utils.serializer_utils import settings
from utils.serializer_utils.exceptions import (
    DjangoValidationError,
    ErrorDetail,
    ReturnDict,
    ReturnList,
    SkipField,
    ValidationError,
)
from utils.serializer_utils.field import (
    empty,
    get_error_detail,
    set_value,
)
from utils.serializer_utils.fields import (
    BooleanField,
    CharField,
    ChoiceField,
    DateField,
    DateTimeField,
    DecimalField,
    DictField,
    Field,
    FileField,
    FloatField,
    IntegerField,
    JSONField,
    ListField,
    NullBooleanField,
    SerializerMethodField,
    TimeField,
)
from utils.serializer_utils.functional import cached_property
from utils.serializer_utils.relations import PKOnlyObject
from utils.serializer_utils.utils import representation
from utils.serializer_utils.utils.serializer_helpers import (
    BindingDict,
    BoundField,
    JSONBoundField,
    NestedBoundField,
)

LIST_SERIALIZER_KWARGS: TupleStr = (
    "read_only", "write_only", "required", "default", "initial", "source",
    "label", "help_text", "style", "error_messages", "allow_empty",
    "instance", "data", "partial", "context", "allow_null",
)
""" 列表序列化器关键字参数 """


class BaseSerializer(Field):
    """
    BaseSerializer 类提供了一个最小的类，可用于编写自定义序列化器实现

    请注意，我们强烈限制序列化器上可能使用的操作属性的顺序，以强制正确使用

    +-------------------+------------------------------+------------+
    | Method            | Have `data=`                 | No `data=` |
    +===================+==============================+============+
    | `.is_valid()`     | √                            | ×          |
    +-------------------+------------------------------+------------+
    | `.initial_data`   | √                            | ×          |
    +-------------------+------------------------------+------------+
    | `.validated_data` | After call `is_valid()` is √ | ×          |
    +-------------------+------------------------------+------------+
    | `errors`          | After call `is_valid()` is √ | ×          |
    +-------------------+------------------------------+------------+
    | `.data`           | After call `is_valid()` is √ | √          |
    +-------------------+------------------------------+------------+
    """

    def __init__(
        self,
        instance=None,
        data=empty,
        **kwargs,
    ):
        self.instance = instance

        if data is not empty:
            self.initial_data = data

        self.partial = kwargs.pop("partial", False)
        self._context = kwargs.pop("context", {})
        kwargs.pop("many", None)
        super().__init__(**kwargs)

    def __new__(cls, *args, **kwargs) -> Field:
        # 我们重写此方法，以便在设置 `many=True` 时自动创建 `ListSerializer` 类
        if kwargs.pop("many", False):
            return cls.many_init(*args, **kwargs)

        return super().__new__(cls, *args, **kwargs)

    @classmethod
    def many_init(cls, *args, **kwargs):
        """
        这个方法实现了在使用 `many=True` 时创建 `ListSerializer` 父类

        如果需要控制传递给父类和传递给子类的关键字参数，可以对其进行自定义
        需要注意的是，我们在传递参数时非常谨慎，尽可能将大多数参数传递给父类和子类，以尽可能覆盖一般情况

        如果要重写此方法，您可能需要更简单的方法，例如：

        >>> @classmethod
        ... def many_init(cls, *args, **kwargs):
        ...     kwargs["child"] = cls()
        ...     return CustomListSerializer(*args, **kwargs)
        """

        allow_empty = kwargs.pop("allow_empty", None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            "child": child_serializer,
        }

        if allow_empty is not None:
            list_kwargs["allow_empty"] = allow_empty

        list_kwargs.update(
            {
                key: value
                for key, value in kwargs.items()
                if key in LIST_SERIALIZER_KWARGS
            }
        )

        meta = getattr(cls, "Meta", None)
        list_serializer_class = getattr(meta, "list_serializer_class", ListSerializer)

        return list_serializer_class(*args, **list_kwargs)

    def to_internal_value(self, data):
        raise NotImplementedError("`to_internal_value()` must be implemented.")

    def to_representation(self, instance):
        raise NotImplementedError("`to_representation()` must be implemented.")

    def update(self, instance, validated_data):
        raise NotImplementedError("`update()` must be implemented.")

    def create(self, validated_data):
        raise NotImplementedError("`create()` must be implemented.")

    def save(self, **kwargs):
        assert not hasattr(self, "save_object"), (
            "Serializer `%s.%s` has old-style version 2 `.save_object()` "
            "that is no longer compatible with REST framework 3. "
            "Use the new-style `.create()` and `.update()` methods instead." %
            (self.__class__.__module__, self.__class__.__name__)
        )

        assert hasattr(self, "_errors"), (
            "You must call `.is_valid()` before calling `.save()`."
        )

        assert not self.errors, (
            "You cannot call `.save()` on a serializer with invalid data."
        )

        # 防止错误使用 `serializer.save(commit=False)`
        assert "commit" not in kwargs, (
            "'commit' is not a valid keyword argument to the 'save()' method. "
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
            "You can also pass additional keyword arguments to 'save()' if you "
            "need to set extra attributes on the saved model instance. "
            "For example: 'serializer.save(owner=request.user)'.'"
        )

        assert not hasattr(self, "_data"), (
            "You cannot call `.save()` after accessing `serializer.data`."
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
        )

        validated_data = {
            **self.validated_data,
            **kwargs,
        }

        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                "`update()` did not return an object instance."
            )

        else:
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                "`create()` did not return an object instance."
            )

        return self.instance

    def is_valid(self, raise_exception: bool = False) -> bool:
        assert not hasattr(self, "restore_object"), (
            "Serializer `%s.%s` has old-style version 2 `.restore_object()` "
            "that is no longer compatible with REST framework 3. "
            "Use the new-style `.create()` and `.update()` methods instead." %
            (self.__class__.__module__, self.__class__.__name__)
        )

        assert hasattr(self, "initial_data"), (
            "Cannot call `.is_valid()` as no `data=` keyword argument was "
            "passed when instantiating the serializer instance."
        )

        if not hasattr(self, "_validated_data"):
            try:
                self._validated_data = self.run_validation(self.initial_data)

            except ValidationError as exc:
                self._validated_data = {}  # noqa
                self._errors = exc.detail

            else:
                self._errors = {}  # noqa

        if (
            self._errors
            and raise_exception
        ):
            raise ValidationError(self.errors)

        return not bool(self._errors)

    @property
    def data(self):
        if (
            hasattr(self, "initial_data")
            and not hasattr(self, "_validated_data")
        ):
            msg = (
                "When a serializer is passed a `data` keyword argument you "
                "must call `.is_valid()` before attempting to access the "
                "serialized `.data` representation.\n"
                "You should either call `.is_valid()` first, "
                "or access `.initial_data` instead."
            )
            raise AssertionError(msg)

        if not hasattr(self, "_data"):
            if (
                self.instance is not None
                and not getattr(self, "_errors", None)
            ):
                self._data = self.to_representation(self.instance)

            elif (
                hasattr(self, "_validated_data")
                and not getattr(self, "_errors", None)
            ):
                self._data = self.to_representation(self.validated_data)

            else:
                self._data = self.get_initial()  # noqa

        return self._data

    @property
    def errors(self) -> ErrorDetail | ReturnDict | ReturnList | dict | list:
        if not hasattr(self, "_errors"):
            raise AssertionError("You must call `.is_valid()` before accessing `.errors`.")

        return self._errors

    @property
    def validated_data(self):
        if not hasattr(self, "_validated_data"):
            raise AssertionError("You must call `.is_valid()` before accessing `.validated_data`.")

        return self._validated_data


class SerializerMetaclass(type):
    """
    序列化器元类

        - 该元类在类上设置一个名为 `_declared_fields` 的字典
        - 作为类或其任何超类上的属性包含的 `Field` 的任何实例都将包含在 `_declared_fields` 字典中
    """

    @classmethod
    def _get_declared_fields(
        cls,
        bases: TupleType,
        attrs: dict,
    ) -> DictStrAny:
        """
        获取序列化器类中声明的所有字段

        遍历序列化器类的属性字典，将所有继承自 Field 的属性保存到一个字典中，并返回该字典

        Args:
            bases: 序列化器类的基类（通常是 serializers.Serializer 类）
            attrs: 序列化器类的属性字典，其中包含了序列化器类的各个字段，例如 CharField、IntegerField 等

        Returns:
            字段（包含序列化器类中声明的所有字段）
        """

        fields: list[tuple[str, Any]] = [
            (field_name, attrs.pop(field_name))
            for field_name, obj in list(attrs.items())
            if isinstance(obj, Field)
        ]
        """ 要创建的类的属性字典列表（如果该属性是 Field 实例的话） """

        # 根据 _creation_counter 排序
        fields.sort(key=lambda x: getattr(x[1], "_creation_counter"))

        # 确保基类字段不会覆盖 cls attrs
        # 并在继承多个父类时保持字段优先级
        # 例如如果有一个类 class C(A, B)，并且 A 和 B 都定义了 `field`，则使用 A 中的 `field`
        known: SetStr = set(attrs)

        def visit(name: str) -> str:
            known.add(name)

            return name

        base_fields: list[tuple[str, Any]] = [
            (visit(name), f)
            for base in bases if hasattr(base, "_declared_fields")  # 循环 1（遍历所有基类）
            for name, f in getattr(base, "_declared_fields").items() if name not in known  # 循环 2（遍历基类中的字段）
        ]
        """ 基类字段列表 """

        return dict(base_fields + fields)

    def __new__(
        cls,
        name: str,
        bases: TupleType,
        attrs: dict,
    ) -> "SerializerMetaclass":
        """
        创建实例

        Args:
            name: 要创建的类的名称
            bases: 要创建的类的基类（通常是 serializers.Serializer 类）
            attrs: 要创建的类的属性字典，其中包含了序列化器类的各个字段，例如 CharField、IntegerField 等
        """

        attrs["_declared_fields"] = cls._get_declared_fields(bases, attrs)

        return super().__new__(cls, name, bases, attrs)


def as_serializer_error(exc):
    assert isinstance(exc, (ValidationError, DjangoValidationError))

    if isinstance(exc, DjangoValidationError):
        detail = get_error_detail(exc)

    else:
        detail = exc.detail

    if isinstance(detail, Mapping):
        # 如果 errors 是一个字典，我们使用标准的 {key: value 列表}
        # 这里我们确保所有值都是错误列表
        return {
            key: (
                value
                if isinstance(value, (list, Mapping))
                else [value]
            )
            for key, value in detail.items()
        }

    elif isinstance(detail, list):
        # 作为列表引发的错误是非字段错误
        return {settings.NON_FIELD_ERRORS_KEY: detail}

    # 作为字符串引发的错误是非字段错误
    return {settings.NON_FIELD_ERRORS_KEY: [detail]}


class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    """ 序列化器 """

    default_error_messages = {
        "invalid": "无效数据，请输入字段，而不是 {datatype}.",
    }

    @cached_property
    def fields(self):
        """
        {field_name: field_instance} 的字典

            - `fields` 被延迟评估
            - 我们这样做是为了确保即使 Django 的应用程序加载阶段尚未运行，导入使用 ModelSerializers 作为字段的模块时也不会出现问题
        """

        fields = BindingDict(self)

        for key, value in self.get_fields().items():
            fields[key] = value

        return fields

    @property
    def _writable_fields(self) -> Iterator:
        """ 可写的字段列表 """

        for field in self.fields.values():
            if not getattr(field, "read_only", False):
                yield field

    @property
    def _readable_fields(self) -> Iterator:
        """ 可读的字段列表 """

        for field in self.fields.values():
            if not getattr(field, "write_only", False):
                yield field

    def get_fields(self) -> DictStrAny:
        """
        返回 {field_name: field_instance} 的字典

            - 每个新的序列化器都是使用字段实例的克隆创建的
            - 这允许用户动态修改序列化器实例上的字段，而不影响其他每个序列化器实例
        """

        return copy.deepcopy(self._declared_fields)  # noqa

    def get_validators(self) -> list:
        """ 返回验证器可调用列表 """

        # 由延迟评估的 "validators" 属性使用
        meta = getattr(self, "Meta", None)
        validators = getattr(meta, "validators", None)

        if validators:
            return list(validators)

        return []

    def get_initial(self):
        if hasattr(self, "initial_data"):
            # 初始数据可能不是有效类型
            if not isinstance(self.initial_data, Mapping):
                return OrderedDict()

            return OrderedDict(
                [
                    (field_name, field_value)
                    for field_name, field in self.fields.items()
                    if (
                        (field_value := field.get_value(self.initial_data)) is not empty
                        and not field.read_only
                    )
                ]
            )

        return OrderedDict(
            [
                (field.field_name, field.get_initial())
                for field in self.fields.values()
                if not field.read_only
            ]
        )

    def get_value(self, dictionary):
        """ 我们覆盖默认字段访问以支持嵌套 HTML 表单我们覆盖默认字段访问以支持嵌套 HTML 表单 """

        return dictionary.get(self.field_name, empty)

    def run_validation(self, data=empty):
        """
        我们重写默认的 `run_validation`
        因为验证器和 `.validate()` 方法执行的验证应该被强制转换为带有 `non_fields_error` 键的错误字典。
        """

        is_empty_value, data = self.validate_empty_values(data)

        if is_empty_value:
            return data

        value = self.to_internal_value(data)

        try:
            self.run_validators(value)
            value = self.validate(value)
            assert value is not None, ".validate() should return the validated data"

        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

        return value

    def _read_only_defaults(self):
        fields: list[Field] = [
            field
            for field in self.fields.values()
            if (
                field.read_only
                and field.default != empty
                and field.source != "*"
                and "." not in field.source
            )
        ]

        defaults = OrderedDict()

        for field in fields:
            try:
                default = field.get_default()

            except SkipField:
                continue

            defaults[field.source] = default

        return defaults

    def run_validators(self, value):
        """ 在运行验证器之前添加默认值的 read_only 字段 """

        if isinstance(value, dict):
            to_validate = self._read_only_defaults()
            to_validate.update(value)

        else:
            to_validate = value

        super().run_validators(to_validate)

    def to_internal_value(self, data):
        """ 包含基本数据类型的字典 -> 包含本地值（Python 内置的数据类型）的字典 """

        if not isinstance(data, Mapping):
            message = self.error_messages["invalid"].format(datatype=type(data).__name__)

            raise ValidationError({settings.NON_FIELD_ERRORS_KEY: [message]}, code="invalid")

        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields

        for field in fields:
            # 获取自定义的校验器（名称的格式必须为：`validate_xxx`，这里的 xxx 的对应的字段名）
            validate_method = getattr(self, "validate_" + field.field_name, None)
            primitive_value = field.get_value(data)

            try:
                validated_value = field.run_validation(primitive_value)

                if validate_method is not None:
                    validated_value = validate_method(validated_value)

            except ValidationError as exc:
                errors[field.field_name] = exc.detail

            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)

            except SkipField:
                pass

            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret

    def to_representation(self, instance) -> OrderedDict:
        """ 反序列化：对象实例 -> 基本数据类型的字典 """

        ret: OrderedDict = OrderedDict()

        for field in self._readable_fields:
            try:
                attribute = field.get_attribute(instance)

            except SkipField:
                continue

            # 对于 `None` 值，我们跳过 `to_representation` ，以便字段不必显式处理这种情况
            # 对于与 `use_pk_only_optimization` 相关的字段，我们需要解析pk值
            check_for_none = (
                attribute.pk
                if isinstance(attribute, PKOnlyObject)
                else attribute
            )

            if check_for_none is None:
                ret[field.field_name] = None

            else:
                ret[field.field_name] = field.to_representation(attribute)

        return ret

    def validate(self, attrs):  # noqa
        return attrs

    def __repr__(self):
        return representation.serializer_repr(self, indent=1)

    # 以下用于访问序列化器上的 `BoundField` 实例，以便在字段值和字段错误上呈现类似表单的 API

    def __iter__(self) -> Iterator:
        for field in self.fields.values():
            yield self[field.field_name]

    def __getitem__(self, key) -> NestedBoundField | JSONBoundField | BoundField:
        field = self.fields[key]
        value = self.data.get(key)
        error = None

        if hasattr(self, "_errors"):
            error = self.errors.get(key)

        if isinstance(field, Serializer):
            return NestedBoundField(field, value, error)

        if isinstance(field, JSONField):
            return JSONBoundField(field, value, error)

        return BoundField(field, value, error)

    # 在返回对象上包含到序列化器类的反向链接
    # 允许 HTMLFormRenderer 等渲染器获取完整的字段信息

    @property
    def data(self) -> ReturnDict:
        ret = super().data

        return ReturnDict(ret, serializer=self)

    @property
    def errors(self) -> ReturnDict:
        ret = super().errors

        if (
            isinstance(ret, list)
            and len(ret) == 1
            and getattr(ret[0], "code", None) == "null"
        ):
            # Edge case. Provide a more descriptive error than
            # "this field may not be null", when no data is passed.
            detail = ErrorDetail("No data provided", code="null")
            ret = {settings.NON_FIELD_ERRORS_KEY: [detail]}

        return ReturnDict(ret, serializer=self)


class ListSerializer(BaseSerializer):
    """ 列表 序列化器 """

    child = None
    many = True

    default_error_messages = {
        "not_a_list": "应该是 list 格式，而不是 {input_type} 格式",
        "empty": "该列表不能为空",
    }

    def __init__(self, *args, **kwargs) -> None:
        self.child = kwargs.pop("child", copy.deepcopy(self.child))
        self.allow_empty = kwargs.pop("allow_empty", True)
        assert self.child is not None, "`child` is a required argument."
        assert not inspect.isclass(self.child), "`child` has not been instantiated."
        super().__init__(*args, **kwargs)
        self.child.bind(field_name="", parent=self)

    def get_initial(self) -> list:
        if hasattr(self, "initial_data"):
            return self.to_representation(self.initial_data)

        return []

    def get_value(self, dictionary):
        """ 给定输入字典，返回字段值 """

        return dictionary.get(self.field_name, empty)

    def run_validation(self, data=empty):
        """
        我们重写默认的 `run_validation`
        因为验证器和 `.validate()` 方法执行的验证应该被强制转换为带有 `non_fields_error` 键的错误字典。
        """

        is_empty_value, data = self.validate_empty_values(data)

        if is_empty_value:
            return data

        value = self.to_internal_value(data)

        try:
            self.run_validators(value)
            value = self.validate(value)
            assert value is not None, ".validate() should return the validated data"

        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

        return value

    def to_internal_value(self, data):
        """ 包含原始数据类型的字典列表 -> 包含本地值的字典列表 """

        if not isinstance(data, list):
            message = self.error_messages["not_a_list"].format(
                input_type=type(data).__name__
            )

            raise ValidationError(
                detail={settings.NON_FIELD_ERRORS_KEY: [message]},
                code="not_a_list",
            )

        if (
            not self.allow_empty
            and len(data) == 0
        ):
            message = self.error_messages["empty"]
            raise ValidationError(
                detail={settings.NON_FIELD_ERRORS_KEY: [message]},
                code="empty",
            )

        ret = []
        errors = []

        for item in data:
            try:
                validated = self.child.run_validation(item)

            except ValidationError as exc:
                errors.append(exc.detail)

            else:
                ret.append(validated)
                errors.append({})

        if any(errors):
            raise ValidationError(errors)

        return ret

    def to_representation(self, data):
        """ 对象实例列表 -> 包含基本数据类型的字典列表"""

        return [
            self.child.to_representation(item)
            for item in data
        ]

    def validate(self, attrs):  # noqa
        return attrs

    def update(self, instance, validated_data):
        raise NotImplementedError(
            "Serializers with many=True do not support multiple update by "
            "default, only multiple create. For updates it is unclear how to "
            "deal with insertions and deletions. If you need to support "
            "multiple update, use a `ListSerializer` class and override "
            "`.update()` so you can specify the behavior exactly."
        )

    def create(self, validated_data):
        return [
            self.child.create(attrs)
            for attrs in validated_data
        ]

    def save(self, **kwargs):
        """ 保存并返回一个对象实例的列表 """

        # 防止错误使用 `serializer.save(commit=False)`
        assert "commit" not in kwargs, (
            "'commit' is not a valid keyword argument to the 'save()' method. "
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
            "You can also pass additional keyword arguments to 'save()' if you "
            "need to set extra attributes on the saved model instance. "
            "For example: 'serializer.save(owner=request.user)'.'"
        )

        validated_data = [
            {
                **attrs,
                **kwargs,
            }
            for attrs in self.validated_data
        ]

        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                "`update()` did not return an object instance."
            )

        else:
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                "`create()` did not return an object instance."
            )

        return self.instance

    def is_valid(self, raise_exception=False):
        # 此实现与默认实现相同，只是我们使用列表而不是字典作为空情况
        assert hasattr(self, "initial_data"), (
            "Cannot call `.is_valid()` as no `data=` keyword argument was "
            "passed when instantiating the serializer instance."
        )

        if not hasattr(self, "_validated_data"):
            try:
                self._validated_data = self.run_validation(self.initial_data)

            except ValidationError as exc:
                self._validated_data = []  # noqa
                self._errors = exc.detail

            else:
                self._errors = []  # noqa

        if (
            self._errors
            and raise_exception
        ):
            raise ValidationError(self.errors)

        return not bool(self._errors)

    def __repr__(self):
        return representation.list_repr(self, indent=1)

    # 在返回对象上包含到序列化器类的反向链接
    # 允许 HTMLFormRenderer 等渲染器获取完整的字段信息

    @property
    def data(self) -> ReturnList:
        ret = super().data

        return ReturnList(ret, serializer=self)

    @property
    def errors(self) -> ReturnDict | ReturnList:
        ret = super().errors

        if (
            isinstance(ret, list)
            and len(ret) == 1
            and getattr(ret[0], "code", None) == "null"
        ):
            # 边缘情况。当没有传递数据时，提供比 `该字段不能为空` 更具描述性的错误
            detail = ErrorDetail("No data provided", code="null")
            ret = {settings.NON_FIELD_ERRORS_KEY: [detail]}

        if isinstance(ret, dict):
            return ReturnDict(ret, serializer=self)

        return ReturnList(ret, serializer=self)
